#include "connection.h"
#include <QFile>
#include <QNetworkRequest>
#include <QUrl>
#include <QApplication>

//**************************************************************
//
//                    CAM SCANNER - PROJECT
//
// version 1.0 - 25.06.2012 Adriano:
//    : first version
//
//**************************************************************

//**************************************************************
//
//                    CONNECTION PROTOCOL
//
//      CLIENT / PHONE              SERVER / PC
//  [connec] (always 8 bytes) --->
//                            <--- rn101 (readnumber version 1.01)
//    OK, server is there. Now client can close socket or continue.
//  [imagex]                  --->
//  image (bytestream)        --->
//    Image transmitted. Close socket to comunicate the end of file.
//
//**************************************************************

connection::connection(QWidget *parent) :
    QWidget(parent)
{
                        //*****************************************
                        // open file with the ip number
                        //
    QFile fIP("ip.txt");
    if(fIP.open(QIODevice::ReadOnly))
    {
        m_sFileIP = fIP.readLine().trimmed();
        fIP.close();
    }
    else
    {
        m_sFileIP = "";
    }
                        //*****************************************

                        //*****************************************
                        // connect to a webpage, so the wifi will be activated
                        // and the connection will not be closed until this app is closed too
                        //
    m_openWiFiConnection = new QNetworkAccessManager(this);
    m_openWiFiConnection->get(QNetworkRequest(QUrl("http://ovi.petrucci.ch/rn.php")));
    connect(m_openWiFiConnection, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)),
            this, SLOT(networkAccessible(QNetworkAccessManager::NetworkAccessibility)));
                        //*****************************************

                        //*****************************************
                        // Init member variables
                        //
    m_bBlinkState = false;
    m_iCheckedState = 0;
    m_bConnectionOK = false;
                        //*****************************************

                        //*****************************************
                        // Init UI
                        //
    this->setGeometry(0,0,640,280);
    this->setStyleSheet("background-color:black;font-size:14px;");

    QLabel *l = new QLabel(this);
    l->setGeometry(this->geometry());
    l->setText("");
    l->setStyleSheet("background-color:black;border-style:ridge;border-color:gray;border-width:2px;border-radius:10px;");

    m_labelWifi = new QLabel(this);
    m_labelWifi->setGeometry((640-64)/2, 40, 64, 64);
    m_labelWifi->setPixmap(QPixmap(":/images/wireless.png"));

    l = new QLabel(this);
    l->setGeometry(320 + 50, 40, 220, 40);
    l->setText(tr("Server IP number\n(application on PC):"));

    m_lineEdit = new QLineEdit(this);
    m_lineEdit->setGeometry(320 + 50, 90, 180, 40);
    m_lineEdit->setText(m_sFileIP);
    m_lineEdit->setStyleSheet("background-color:#313131;color:#d1d1d1;border-style:ridge;border-color:gray;border-width:2px;border-radius:10px;");
    connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(textEdited()));

    m_labelIP = new QLabel(this);
    m_labelIP->setGeometry(320 + 50 + 180 + 5, 94, 32, 32);
    m_labelIP->setPixmap(QPixmap(":/images/ko.png"));

    m_buttonSearchServer = new QPushButton(this);
    m_buttonSearchServer->setGeometry(50, 60, 220, 40);
    m_buttonSearchServer->setFocusPolicy(Qt::ClickFocus);
    m_buttonSearchServer->setText(tr("Search server on\nWiFi network..."));
    m_buttonSearchServer->setStyleSheet("border-style:ridge;border-color:gray;border-width:2px;border-radius:10px;");
    connect(m_buttonSearchServer, SIGNAL(clicked()), this, SLOT(searchServer()));

    m_buttonConnect = new QPushButton(this);
    m_buttonConnect->setGeometry(320 + 50, 140, 220, 40);
    m_buttonConnect->setFocusPolicy(Qt::ClickFocus);
    m_buttonConnect->setText(tr("CHECK"));
    m_buttonConnect->setStyleSheet("border-style:ridge;border-color:gray;border-width:2px;border-radius:10px;");
    m_buttonConnect->setEnabled(true);
    connect(m_buttonConnect, SIGNAL(clicked()), this, SLOT(tryConnect()));

    m_buttonExit = new QPushButton(this);
    m_buttonExit->setGeometry(320 + 50, 230, 220, 40);
    m_buttonExit->setFocusPolicy(Qt::ClickFocus);
    m_buttonExit->setText(tr("EXIT"));
    m_buttonExit->setStyleSheet("border-style:ridge;border-color:gray;border-width:2px;border-radius:10px;");
    connect(m_buttonExit, SIGNAL(clicked()), this, SLOT(exitApp()));

    m_plainTextEdit = new QPlainTextEdit(this);
    m_plainTextEdit->setReadOnly(true);
    m_plainTextEdit->setGeometry(10,175,320,100);
    m_plainTextEdit->setStyleSheet("font-size:10px;");
    m_plainTextEdit->setPlainText(tr("Start ReadNumber on your PC\nbefore starting this app.\n\nPhone must stay in the same WiFi network."));

    m_timerBlink = new QTimer(this);
    m_timerBlink->setInterval(1000);
    connect(m_timerBlink, SIGNAL(timeout()), this, SLOT(blinkImage()));
    m_timerBlink->start();
                        //*****************************************
}

//*****************************************************************************************************************
// Return the valid IP (the last IP where it was possible to connect)
QString connection::getSavedIP(void)
{
    return m_sFileIP;
}

//*****************************************************************************************************************
// Blink wifi image, so the users see that something is done with the wifi.
// This is a slot, called from the timer
void connection::blinkImage(void)
{
    if(m_bBlinkState)
        m_labelWifi->setPixmap(QPixmap(":/images/wireless.png"));
    else
        m_labelWifi->setPixmap(QPixmap(":/images/wireless2.png"));
    m_bBlinkState = !m_bBlinkState;

                    // If internet is connected, check last ip number
    if(m_iCheckedState==1)
    {
        m_iCheckedState = 2;
        if(m_sFileIP.length()>7)
        {
            if(checkIP())
            {
                m_buttonConnect->setEnabled(true);
            }
        }
    }

                    // if server is connected, emit signal and hide widget
    if(m_bConnectionOK)
    {
                    // timer is set to 1000, if server is connected a 'v' is shown near the IP address.
                    // so, timer is set to 800, and the user can see that the connection was estabilished
        if(m_timerBlink->interval()>900)
        {
            m_timerBlink->setInterval(800);
        }
        else
        {
            emit(connected());
            m_timerBlink->stop();
            this->hide();
        }
    }
}

//*****************************************************************************************************************
// Internet is available and accessible (over GSM or WiFi), now the IP can be checked.
// This is a slot, called from the network manager
void connection::networkAccessible(QNetworkAccessManager::NetworkAccessibility acc)
{
    if(acc == QNetworkAccessManager::Accessible)
    {
        m_iCheckedState = 1;
    }
}

//*****************************************************************************************************************
// Search all servers on this network (from x.x.x.1 to x.x.x.128/256)
// This is a slot, called from the network manager
void connection::searchServer(void)
{
    int iNets = 0;

    if(m_iCheckedState == 0)
    {
                    // if connection is still not etabilished, try again
        m_openWiFiConnection->get(QNetworkRequest(QUrl("http://ovi.petrucci.ch/rn.php")));
        m_plainTextEdit->setPlainText("connection not available now.\nTry again\n\n");
        return;
    }

                    // disable "connect-button"
    m_buttonConnect->setEnabled(false);

                    // reset debug textbox
    m_plainTextEdit->setPlainText("");

                    //*****************************************
                    // search throug all addresses present on the phone
                    // take the first INTERNET (IP4) address as reference address for home network
                    //
    QList<QHostAddress>list = QNetworkInterface::allAddresses();
    for(int i=0;i<list.count();i++)
    {
        if(list[i].protocol() == QAbstractSocket::IPv4Protocol)
        {
            quint32 ip = list[i].toIPv4Address() & 0xff000000;
            if(ip == 0x7F000000) continue;
            iNets++;
            if(m_hostAddress.isNull()) m_hostAddress = list[i];
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + list[i].toString() + " - ");
        }
        else
        {
            qDebug() << list[i].toString();
        }
    }

    if(iNets == 0)
    {
        m_plainTextEdit->setPlainText("ERROR:\n- no WiFi detected (is wireless off?).\n");
        m_buttonConnect->setEnabled(false);
        return;
    }
                    //*****************************************



                    //*****************************************
                    // Create 128/256 sockets (only 8 can be created at once on symbian)
                    // and check if there is a server on this address
                    // TODO: It works well on the simulator, on the phone I can't find this server also if the address is correct!
                    //
    m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nsearch server (" + m_hostAddress.toString() + ")... ");
    QString sTempText = m_plainTextEdit->toPlainText();

    int imaxIP = 128;
    QTcpSocket *s[imaxIP];
    bool bConn[imaxIP];
    int iLastIPNumber = 0;
    bool bFound = false;

    for(int k=0;k<imaxIP/4;k++)
    {
        for(int i=0;i<8;i++)
        {
            s[i+iLastIPNumber] = new QTcpSocket(this);
            QHostAddress hostAddress2 = m_hostAddress;
            hostAddress2.setAddress((m_hostAddress.toIPv4Address() & 0xffffff00) | (iLastIPNumber+i));
            s[i+iLastIPNumber]->connectToHost(hostAddress2, 7186, QIODevice::ReadWrite);
        }
                    // wait 500ms (should be enough) if it can't find a device on this number, continue to search
        s[7 + iLastIPNumber]->waitForConnected(500);
        for(int i=0;i<8;i++)
        {
            if(s[i + iLastIPNumber]->state()==3)
            {
                bConn[i+ iLastIPNumber]=true;
                s[i + iLastIPNumber]->write("[connec]");
                m_plainTextEdit->setPlainText(sTempText + "\nfound: " + QString::number((m_hostAddress.toIPv4Address() & 0xffffff00) | (iLastIPNumber+i), 16) + " - " + QString::number(s[i+iLastIPNumber]->state()));
                bFound = true;
            }
            else
            {
                bConn[i + iLastIPNumber]=false;
                s[i + iLastIPNumber]->close();
            }
        }

        if(!bFound)
        {
            m_plainTextEdit->setPlainText(sTempText + QString::number((int)(100*k/(imaxIP/4-1))) + "%");
            QApplication::processEvents();  // leave time to update the text
        }
        iLastIPNumber += 8;
        QApplication::processEvents();      // leave time to let blink the wifi label image
        if(bFound) break;
    }
                    //*****************************************


                    //*****************************************
                    // Create 128/256 sockets (only 8 can be created at once on symbian)
                    // and check if there is a server on this address
                    // TODO: It works well on the simulator, on the phone I can't find this server also if the address is correct!
                    //
    for(int k=0;k<qMin(iLastIPNumber,imaxIP);k++)
    {
        if(bConn[k])
        {
            QString answer;
            s[k]->waitForReadyRead(1000);
            answer = QString::fromAscii(s[k]->readAll());
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nserver: " + answer);
            if(answer.at(0) == 'r' && answer.at(1) == 'n')
            {
                quint32 add = m_hostAddress.toIPv4Address();
                m_lineEdit->setText(QString::number(add>>8*3) +
                                      "." + QString::number((add&0x00ff0000)>>8*2) +
                                      "." + QString::number((add&0x0000ff00)>>8*1) +
                                      "." + QString::number((add&0x000000ff)>>8*0));

                QFile fIP("ip.txt");
                if(fIP.open(QIODevice::WriteOnly))
                {
                    QString output = m_lineEdit->text() + "\n";
                    fIP.write(output.toAscii());
                    fIP.close();
                }
            }
            s[k]->close();
        }
    }
                    //*****************************************

    m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nsearching is over.\n");
                    // re-enable 'connect' button
    m_buttonConnect->setEnabled(true);
}

//*****************************************************************************************************************
// Send im (image) to the server as bytestream and close socket.
//
void connection::sendImageOverWiFi(QImage im)
{
    m_plainTextEdit->setPlainText("");

                    //*****************************************
                    // search throug all addresses present on the phone
                    // take the first INTERNET (IP4) address as reference address for home network
                    //
    QList<QHostAddress>list = QNetworkInterface::allAddresses();
    for(int i=0;i<list.count();i++)
    {
        if(list[i].protocol() == QAbstractSocket::IPv4Protocol)
        {
            quint32 ip = list[i].toIPv4Address() & 0xff000000;
            if(ip == 0x7F000000) continue;
            if(m_hostAddress.isNull()) m_hostAddress = list[i];
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + list[i].toString());
        }
    }
                    //*****************************************

    m_hostAddress.setAddress(m_lineEdit->text());
                    // save image to RAM (in Symbian, d:\ is the RAM drive. e: is memory card)
    im.save("d:\\image.jpg");

    m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nconnecting...");


                    //*****************************************
                    // create a socket connection to the server
                    // send [connec], [imagex] and the image file.
                    //
    QTcpSocket *s = new QTcpSocket(this);
    s->connectToHost(m_hostAddress, 7186, QIODevice::ReadWrite);
    if(s->waitForConnected())
    {
        qint64 bytesWritten = 0;
        bytesWritten = s->write("[connec]", QString("[connec]").length());
        s->waitForBytesWritten();
        s->waitForReadyRead(1000);
        m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + QString::fromAscii(s->readAll()));
        bytesWritten = s->write("[imagex]", QString("[imagex]").length());
        s->waitForBytesWritten();
        s->waitForReadyRead(200);   // there is nothing to read. Leave time to the server to parse [imagex]

        QFile f("d:\\image.jpg");
        if(f.open(QIODevice::ReadOnly))
        {
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nsending image...");
            bytesWritten = f.bytesAvailable();
            bytesWritten = s->write(f.readAll(), bytesWritten);
            if(bytesWritten<0)
                m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + s->errorString());
            f.close();
            s->waitForBytesWritten();
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nimage sent!");

            s->waitForReadyRead(200); // there is nothing to read. Leave time to the server to read all, before closing socket.
        }
        else
        {
            m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nError, can't read image!");
        }

        s->close();
    }
    else
    {
        m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + s->errorString());
    }
                    //*****************************************
}


//*****************************************************************************************************************
// Close app
// This is a slot, called from the exit button
void connection::exitApp()
{
    QApplication::closeAllWindows();
}


//*****************************************************************************************************************
// Check if a connection with the server can be estabilished over this IP.
// This function is called every time the client will start the app. A connection with the server must be there if
//   the app will startup.
bool connection::checkIP(void)
{
    bool bRet = false;
    int iNets = 0;
    m_plainTextEdit->setPlainText("");

    if(m_iCheckedState == 0)
    {
        m_openWiFiConnection->get(QNetworkRequest(QUrl("http://ovi.petrucci.ch/rn.php")));
    }

    QList<QHostAddress>list = QNetworkInterface::allAddresses();
    for(int i=0;i<list.count();i++)
    {
        if(list[i].protocol() == QAbstractSocket::IPv4Protocol)
        {
            if(m_hostAddress.isNull())
            {
                quint32 ip = list[i].toIPv4Address() & 0xff000000;
                if(ip == 0x7F000000) continue;
                iNets++;
                m_hostAddress = list[i];
                m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + list[i].toString());
            }
        }
    }

    if(iNets == 0)
    {
        m_plainTextEdit->setPlainText("ERROR:\n- no WiFi detected (wireless is off).");
    }

    m_hostAddress.setAddress(m_lineEdit->text());

    m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nconnecting to " + m_lineEdit->text() + "...");

    QApplication::processEvents();

    QTcpSocket *s = new QTcpSocket(this);
    s->connectToHost(m_hostAddress, 7186, QIODevice::ReadWrite);
    if(s->waitForConnected(10000))
    {
        s->write("[connec]", QString("[connec]").length());
        s->waitForReadyRead();
        QString sReply = QString::fromAscii(s->readAll());
        m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\n" + sReply);

        if(sReply.trimmed() == "rn101")
        {
            m_labelIP->setPixmap(QPixmap(":/images/ok.png"));
            m_buttonConnect->setEnabled(false);
            m_bConnectionOK = true;
            bRet = true;
        }
        else
        {
            m_labelIP->setPixmap(QPixmap(":/images/ko.png"));
            m_buttonConnect->setEnabled(true);
        }
        m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nconnection verified.");
    }
    else
    {
        m_plainTextEdit->setPlainText(m_plainTextEdit->toPlainText() + "\nCONNECTION FAILED!");
        m_labelIP->setPixmap(QPixmap(":/images/ko.png"));
        m_buttonConnect->setEnabled(true);
    }
    s->close();

    return bRet;
}


//*****************************************************************************************************************
// Text was edited, try to connect again
// This is a slot, called from the edittext
void connection::textEdited(void)
{
    if(checkIP())
    {
            // if successfull, save IP to a file, so the next time it will be there again
        QFile fIP("ip.txt");
        if(fIP.open(QIODevice::WriteOnly))
        {
            QString output = m_lineEdit->text() + "\n";
            fIP.write(output.toAscii());
            fIP.close();
        }
    }
}

//*****************************************************************************************************************
// Try to connect to the server
// This is a slot, called from the connect button
void connection::tryConnect(void)
{
    checkIP();
}

//*****************************************************************************************************************
// Get message got from the function
// This is a function called from other classes, so they can output the debug info.
QString connection::getMessage(void)
{
    return m_plainTextEdit->toPlainText();
}
